#pragma once

#include "sock.hpp"
#include <iostream>
#include <functional>
#include <poll.h>
#include <unistd.h>
#include <sys/epoll.h>

#define FD_NUM 1024

using namespace std;

using func = function<string(const string&)>;

const int defaultfd = -1;
const int defaultnum = 64;

/*
epoll对比select和poll的优势
1.在每轮调用前不用重置任何结构
2.每当有事件就绪时 不用将所有sock进行遍历检测哪一个sock的事件就绪 就绪队列中的sock都是就绪状态
*/
class epollServer
{
public:
    epollServer(uint16_t port, func f)
    : _port(port),
      _f(f),
      _listensock(-1),
      _epfd(-1),
      _revs(nullptr)
    {}

    ~epollServer()
    {
        if (_listensock != defaultfd)
            close(_listensock);
        if (_epfd != defaultfd)
            close(_epfd);
        if (_revs != nullptr)
            delete[] _revs;
    }

    void initServer()
    {
        _listensock = Sock::Socket();
        Sock::Bind(_listensock, _port);
        Sock::Listen(_listensock);
        _epfd = epoll_create(1);
        if (_epfd == -1)//size > 0即可
        {
            cout << "epoll create error" << endl;
            exit(EPOLL_CRE_ERR);
        }
        struct epoll_event ev;
        ev.data.fd = _listensock;
        ev.events = EPOLLIN;
        epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock, &ev);

        //往后就绪的事件将放到这个数组中 就绪队列
        _revs = new struct epoll_event[defaultnum];
    }

    void startServer()
    {
        while (true)
        {
            //select在调用前需要重新设置位图结构 而poll的events在添加fd时就已设置好 除非要新增事件 否则无需更改
            int n = epoll_wait(_epfd, _revs, defaultnum, -1);//>0 时限内等待  =0 非阻塞 -1阻塞
            switch (n)
            {
            case 0:
                cout << "time out" << endl;
                break;
            case -1:
                cout << "epoll error: " << strerror(errno) << endl;
                break;
            default:
                cout << "have event ready" << endl;
                handlerEvent(n);
                break;
            }
        }        
    }

    //{} [] () - & 
    void handlerEvent(int readyNum)
    {
        for (int i = 0; i < readyNum; i++)
        {
            struct epoll_event ev = _revs[i];
            if (!(ev.events & EPOLLIN))//只关心读事件
                continue;
            if (ev.data.fd == _listensock)
                Accepter();
            else
                Receiver(ev.data.fd);
            //每次处理完就绪事件 例如读事件将数据全部读取完成那么该事件会从就绪队列中删除
        }
    }

    void Accepter()
    {
        struct sockaddr_in client;
        memset(&client, 0, sizeof client);
        int sock = Sock::Accept(_listensock, &client);
        string clientip = inet_ntoa(client.sin_addr);
        uint16_t clientport = ntohs(client.sin_port);
        cout << "accept a new link " << clientip << "[" << clientport << "]" << endl;
        struct epoll_event ev;
        ev.data.fd = sock;
        ev.events = EPOLLIN;
        epoll_ctl(_epfd, EPOLL_CTL_ADD, sock, &ev);//将新连接添加到epoll模型中并关心读事件
    }

    void Receiver(int sock)
    {
        char buffer[1024];
        int n = recv(sock, buffer, sizeof buffer, 0);
        if (n > 0)
        {
            buffer[n - 1] = 0;
            cout << "#client: " << buffer << endl;
        }
        else if (n == 0)
        {
            cout << "client quit" << endl;
            epoll_ctl(_epfd, EPOLL_CTL_DEL, sock, nullptr);//将连接从epoll模型中删除
            close(sock);//先从模型中删除再关闭sock
            return;
        }
        else
        {
            cout << "recv error: " << strerror(errno) << endl;
            epoll_ctl(_epfd, EPOLL_CTL_DEL, sock, nullptr);
            close(sock);
            return;
        }
        string response = _f(buffer);
        //write(sock, response.c_str(), response.size());
        send(sock, response.c_str(), response.size(), 0);
    }


private:
    int _listensock;
    uint16_t _port;
    int _epfd;
    struct epoll_event* _revs;
    func _f;
};
